﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Filter types
    /// </summary>
    public enum NoiseFilterType
    {
        /// <summary>
        /// Ideal filter
        /// </summary>
        Ideal,

        /// <summary>
        /// Butterworth filter
        /// </summary>
        Butterworth,

        /// <summary>
        /// Gaussian Filter
        /// </summary>
        Gaussian
    }

    /// <summary>
    /// Band reject or pass
    /// </summary>
    public enum BandType
    {
        /// <summary>
        /// Band reject
        /// </summary>
        Reject,

        /// <summary>
        /// Band pass
        /// </summary>
        Pass
    }

    /// <summary>
    /// Noise filters class
    /// </summary>
    public class SignalNoiseFilters
    {
        /// <summary>
        /// Notch filter.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="notchRadius">The notch radius.</param>
        /// <param name="notchCenter">The notch center.</param>
        /// <param name="filterType">Type of the filter.</param>
        /// <param name="mask">The mask.</param>
        /// <param name="butterworthN">The butterworth N.</param>
        /// <param name="bandType">Type of the band.</param>
        /// <returns>
        /// Notch circles
        /// </returns>
        public static ImageStructure[,] NotchFilter(ImageStructure[,] originalImage, int notchRadius, Point notchCenter, NoiseFilterType filterType, ref double[,] mask, int butterworthN, BandType bandType)
        {
            int height = originalImage.GetLength(0);
            int width = originalImage.GetLength(1);
            ImageStructure[,] notchImage = new ImageStructure[height, width];

            double radiusOne = 0, radiusTwo = 0;
            int halfHeight = height / 2;
            int halfWidth = width / 2;
           
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    radiusOne = Math.Pow(Math.Pow(j - halfWidth + notchCenter.X, 2.0) + Math.Pow(i - halfHeight + notchCenter.Y, 2.0), 0.5);
                    radiusTwo = Math.Pow(Math.Pow(j - halfWidth - notchCenter.X, 2.0) + Math.Pow(i - halfHeight - notchCenter.Y, 2.0), 0.5);

                    switch (filterType)
                    {
                        case NoiseFilterType.Ideal:
                            if (radiusOne > notchRadius && radiusTwo > notchRadius)
                            {
                                notchImage[i, j].R = 255;
                                notchImage[i, j].G = 255;
                                notchImage[i, j].B = 255;
                                mask[i, j] = 1;
                            }
                            else
                            {
                                mask[i, j] = 0;
                            }

                            break;
                        case NoiseFilterType.Butterworth:
                            mask[i, j] = Math.Pow((notchRadius * notchRadius) / (radiusOne * radiusTwo), butterworthN) + 1;
                            mask[i, j] = 1 / mask[i, j];
                            notchImage[i, j].R = (byte)(mask[i, j] * 255);
                            notchImage[i, j].G = (byte)(mask[i, j] * 255);
                            notchImage[i, j].B = (byte)(mask[i, j] * 255);
                            break;
                        case NoiseFilterType.Gaussian:
                            mask[i, j] = 1 - Math.Exp(-1 * 0.5 * radiusTwo * radiusOne / (notchRadius * notchRadius));
                            notchImage[i, j].R = (byte)(mask[i, j] * 255);
                            notchImage[i, j].G = (byte)(mask[i, j] * 255);
                            notchImage[i, j].B = (byte)(mask[i, j] * 255);
                            break;
                        default:
                            break;
                    }

                    if (bandType == BandType.Pass)
                    {
                        mask[i, j] = Math.Abs(1 - mask[i, j]);
                        notchImage[i, j].R = (byte)(255 - notchImage[i, j].R);
                        notchImage[i, j].G = (byte)(255 - notchImage[i, j].G);
                        notchImage[i, j].B = (byte)(255 - notchImage[i, j].B);
                    }
                }
            }

            return notchImage;
        }

        /// <summary>
        /// Bands the filter.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="circleRadius">The circle radius.</param>
        /// <param name="bandWidth">Width of the band.</param>
        /// <param name="filtertype">The filtertype.</param>
        /// <param name="mask">The mask.</param>
        /// <param name="butterworthN">The butterworth N.</param>
        /// <param name="bandType">Type of the band.</param>
        /// <returns>
        /// BandFilter Image
        /// </returns>
        public static ImageStructure[,] BandFilter(ImageStructure[,] originalImage, int circleRadius, int bandWidth, NoiseFilterType filtertype, ref double[,] mask, int butterworthN, BandType bandType)
        {
            int height = originalImage.GetLength(0);
            int width = originalImage.GetLength(1);

            ImageStructure[,] newImage = new ImageStructure[height, width];
            int centerX = width / 2;
            int centerY = height / 2;

            int x, y;
            double radius;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    x = j - centerX;
                    y = i - centerY;
                    radius = Math.Sqrt((double)((y * y) + (x * x)));
                    switch (filtertype)
                    {
                        case NoiseFilterType.Ideal:
                            if (radius < (circleRadius - (bandWidth / 2)) || radius > (circleRadius + (bandWidth / 2)))
                            {
                                mask[i, j] = 1;
                                newImage[i, j].R = 255;
                                newImage[i, j].G = 255;
                                newImage[i, j].B = 255;
                            }
                            else
                            {
                                mask[i, j] = 0.0;
                            }

                            break;
                        case NoiseFilterType.Butterworth:
                            mask[i, j] = 1 + Math.Pow((radius * bandWidth / ((radius * radius) - (circleRadius * circleRadius))), 2 * butterworthN);
                            mask[i, j] = 1 / mask[i, j];
                            newImage[i, j].R = (byte)(mask[i, j] * 255);
                            newImage[i, j].G = (byte)(mask[i, j] * 255);
                            newImage[i, j].B = (byte)(mask[i, j] * 255);

                            break;
                        case NoiseFilterType.Gaussian:
                            mask[i, j] = Math.Pow(((radius * radius) - (circleRadius * circleRadius)) / (radius * bandWidth), 2);
                            mask[i, j] = 1 - Math.Exp(-1 * 0.5 * mask[i, j]);
                            newImage[i, j].R = (byte)(mask[i, j] * 255);
                            newImage[i, j].G = (byte)(mask[i, j] * 255);
                            newImage[i, j].B = (byte)(mask[i, j] * 255);

                            break;
                        default:
                            break;
                    }

                    if (bandType == BandType.Pass)
                    {
                        mask[i, j] = Math.Abs(1 - mask[i, j]);
                        newImage[i, j].R = (byte)(255 - newImage[i, j].R);
                        newImage[i, j].G = (byte)(255 - newImage[i, j].G);
                        newImage[i, j].B = (byte)(255 - newImage[i, j].B);
                    }
                }
            }

            return newImage;
        }
    }
}